#pragma once

#define MAX 5

template<class T>
class BlockQueue
{
public:
    

      BlockQueue(const size_t capacity = MAX)
      :_capacity(capacity)
      {
         pthread_cond_init(&_consumer,nullptr);
         pthread_cond_init(&_producer,nullptr);
         pthread_mutex_init(&_mutex,nullptr);
      }

      void push(const T& _data) // 输入型参数我们一般喜欢用 引用
      {
          // 要push _data那就必须判断队列有没有满
          pthread_mutex_lock(&_mutex);  //加锁，其他线程禁止进入队列操作

          // 细节2，这里用if判断是不够安全的，如果用的是broadcast唤醒10个进程，那么一下就会被抽走10个数据，改用while，连续判断
          while(_queue.size() == _capacity)
          {
              // 已经满了禁止push
              pthread_cond_wait(&_producer,&_mutex);
              //细节1，解锁_mutex，当该线程等待消费者的时候，要让消费者控制操作队列啊，所以要解锁
          }

         _queue.push(_data);

          // 能走到这里，说明至少push了一个，可以唤醒消费者来消费了
          pthread_cond_signal(&_consumer);

          pthread_mutex_unlock(&_mutex);
      }

      void pop(T* _data)  // 输出型参数我们一般喜欢用 指针
      {
         pthread_mutex_lock(&_mutex);
         while(_queue.empty())
         {
             // 空了禁止拿数据
             pthread_cond_wait(&_consumer,&_mutex);
         }

         // 这里会调用 operator赋值函数 , 没有资源，所以用编译器默认生成的即可
         *_data = _queue.front();
         _queue.pop();
         // 能走到这里，说明至少有一个空位，可以唤醒生产者来生产了
         pthread_cond_signal(&_producer);

         pthread_mutex_unlock(&_mutex);
      }

      ~BlockQueue()
      {
          pthread_mutex_destroy(&_mutex);
          pthread_cond_destroy(&_producer);
          pthread_cond_destroy(&_consumer);
      }

private:
   std::queue<T> _queue;
   pthread_cond_t _consumer;
   pthread_cond_t _producer;
   pthread_mutex_t _mutex;
   size_t _capacity;
};

// 这个类要封装两个阻塞队列,方便所有线程看到
template<class T,class S>
class BlockQueueS
{
public:
     BlockQueueS()
     {}

     BlockQueueS(const BlockQueue<T>& bq1,const BlockQueue<T>& bq2)
     :_bq1(bq1)
     ,_bq2(bq2)
     {}


public:
   BlockQueue<T> *_bq1;  // 任务队列
   BlockQueue<S> *_bq2;  // 保存队列
};